package db

import (
	"fmt"
	"net/url"
	"sync"
	"time"

	_ "github.com/ClickHouse/clickhouse-go"
	"github.com/jmoiron/sqlx"
)

var (
	clickhouseConns     map[string]*sqlx.DB
	clickhouseConnMutex sync.Mutex
)

type ClickhouseConn struct {
	Conn    *sqlx.DB
	connStr string
}

func (this *ClickhouseConn) Clone() (*ClickhouseConn, error) {
	return newClickhouseConnStr(this.connStr)
}

/**
var count int
err := db.Get(&count, "SELECT count(*) FROM message")
if err != nil {
	t.Fatal(err)
}
if count != len(messages) {
	t.Fatalf("Expected %d messages in DB, found %d", len(messages), count)
}
*/

/**
person := &Person{}
err = db.Get(person, "SELECT * FROM person WHERE first_name=$1", "does-not-exist")
if err == nil {
	t.Fatal("Should have got an error for Get on non-existent row.")
}
*/
func (this *ClickhouseConn) Get(dest interface{}, query string, args ...interface{}) error {
	return this.Conn.Get(dest, query, args...)
}

func (this *ClickhouseConn) Select(dest interface{}, query string, args ...interface{}) error {
	return this.Conn.Select(dest, query, args...)
}

func (this *ClickhouseConn) Each(query string, dest interface{}, cb func(err error) bool) error {
	rows, err := this.Conn.Queryx(query)
	if err != nil {
		return err
	}

	for rows != nil && rows.Next() {
		err := rows.StructScan(dest)
		if !cb(err) {
			break
		}
	}

	return err
}

func NewClickHouseByShardInfo(info *ShardInfo) (*ClickhouseConn, error) {
	// tcp://host1:9000?username=user&password=qwerty&database=clicks&read_timeout=10&write_timeout=20&alt_hosts=host2:9000,host3:9000
	// connect, err := sqlx.Open("clickhouse", "tcp://192.168.31.111:9234?debug=true")

	params := url.Values{}
	params.Add("username", info.Username)
	params.Add("password", info.Password)
	params.Add("database", info.Dbname)

	var connStr = fmt.Sprintf("tcp://%s:%d?%s&debug=true",
		info.Host, info.Port, params.Encode())

	return newClickhouseConnStr(connStr)
}

func newClickhouseConnStr(connStr string) (*ClickhouseConn, error) {
	clickhouseConnMutex.Lock()
	defer clickhouseConnMutex.Unlock()
	// tcp://host1:9000?username=user&password=qwerty&database=clicks&read_timeout=10&write_timeout=20&alt_hosts=host2:9000,host3:9000
	// connect, err := sqlx.Open("clickhouse", "tcp://192.168.31.111:9234?debug=true")

	if clickhouseConns == nil {
		clickhouseConns = make(map[string]*sqlx.DB)
	}

	if conn, ok := clickhouseConns[connStr]; ok {
		return &ClickhouseConn{Conn: conn, connStr: connStr}, nil
	} else {

		if connect, err := sqlx.Open("clickhouse", connStr); err != nil {
			return nil, err
		} else {
			// 设置连接池的空闲数大小
			connect.SetMaxIdleConns(5)
			// 设置最大打开连接数
			connect.SetMaxOpenConns(80)
			// 15分钟自动结束，防止被mysql主动关闭，造成invalid connection错误
			connect.SetConnMaxLifetime(time.Minute * 15)

			clickhouseConns[connStr] = connect

			return &ClickhouseConn{Conn: connect, connStr: connStr}, err
		}
	}
}

//func NewClickhouseConn(hostPort, opt string) (*ClickhouseConn, error) {
//	if !strings.HasPrefix(opt, "&") {
//		opt = fmt.Sprintf("&%s", opt)
//	}
//
//	connStr := fmt.Sprintf("tcp://%s?debug=true%s", hostPort, opt)
//	logger.Infof("ck conn str is -> `%s`", connStr)
//	return newClickhouseConnStr(connStr)
//}
